package com.net.web.server.server;

import com.net.common.entity.Message;
import com.net.common.exception.NetRuntimeException;
import com.net.common.util.CloseableUtils;
import com.net.common.util.MessageSerializableUtils;
import com.net.web.server.strategy.IMessageStrategy;
import com.net.web.server.strategy.impl.MessageContext;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.charset.StandardCharsets;

import static com.net.common.constant.Constant.MessageType.ERROR;
import static com.net.common.constant.Constant.MessageType.CLOSE;
import static com.net.web.server.config.ServerMapContext.*;

/**
 * 监听客户端消息
 *
 * @author hjh
 */
@Slf4j
public class ServerSocketRunnable implements Runnable {
    /**
     * 客户端socket
     */
    private final Socket clientSocket;

    private final InputStream clientIn;

    private final OutputStream clientOut;
    /**
     * 客户端标识
     */
    private final Long sessionId;

    @SneakyThrows
    public ServerSocketRunnable(Socket socket, Long sessionId) {
        this.clientSocket = socket;
        this.clientIn = socket.getInputStream();
        this.clientOut = socket.getOutputStream();
        this.sessionId = sessionId;
    }

    @Override
    public void run() {
        while (!clientSocket.isClosed() && clientSocket.isConnected()) {
            try {
                Message message = MessageSerializableUtils.listenerSerializableMessage(clientIn);
                if (CLOSE.equals(message.getMessageType())) {
                    break;
                }
                // 根据不同消息类型发送消息
                IMessageStrategy<Message> iMessageStrategy = MessageContext.getMessageStrategy(message.getMessageType());
                iMessageStrategy.execute(message, clientSocket, sessionId);
            } catch (Exception e) {
                if (e instanceof NetRuntimeException) {
                    MessageSerializableUtils.sendSerializableMessage(clientOut, () -> new Message(ERROR, e.getMessage().getBytes(StandardCharsets.UTF_8)));
                }
                log.error("关闭客户端{},原因：{}", clientSocket.getPort(), e.getMessage());
                break;
            }
        }

        // 关闭缓存并释放(clientOut, clientIn 如果打开了最好单独释放避免内存泄露)
        CloseableUtils.close(clientOut, clientIn, clientSocketMap.remove(sessionId), proxyServerSocketMap.remove(sessionId));
        String ip = clientSocket.getInetAddress().getHostAddress();
        if (clientIpMap.containsKey(ip)) {
            clientIpMap.put(ip, clientIpMap.get(ip) - 1);
        }
    }
}
